﻿/*
 * 作者：新生命开发团队（http://www.newlifex.com/）
 * 
 * 版权：版权所有 (C) 新生命开发团队 2002-2014
 * 
 * 修改：海洋饼干（cuteant@outlook.com）
*/

using System;
using System.Windows.Forms;

namespace CuteAnt.Log
{
	/// <summary>文本控件输出日志</summary>
	public class TextControlLog : Logger
	{
		private Control _Control;

		/// <summary>文本控件</summary>
		public Control Control { get { return _Control; } set { _Control = value; } }

		private Int32 _MaxLines = 1000;

		/// <summary>最大行数，超过该行数讲清空文本控件。默认1000行</summary>
		public Int32 MaxLines { get { return _MaxLines; } set { _MaxLines = value; } }

		//private Boolean _Timestamp;
		///// <summary>是否输出时间戳</summary>
		//public Boolean Timestamp { get { return _Timestamp; } set { _Timestamp = value; } }

		/// <summary>写日志</summary>
		/// <param name="level">日志级别</param>
		/// <param name="msg">信息</param>
		protected override void OnWrite(LogLevel level, String msg)
		{
			WriteLog(Control, WriteLogEventArgs.Current.Set(level, msg, null, true).ToString() + Environment.NewLine, MaxLines);
		}

		/// <summary>写带格式信息的日志</summary>
		/// <param name="level">日志级别</param>
		/// <param name="formatMsg">格式化字符串</param>
		/// <param name="args">格式化参数</param>
		protected override void OnWrite(LogLevel level, String formatMsg, params Object[] args)
		{
			OnWrite(level, null, formatMsg, args);
		}

		/// <summary>写异常日志</summary>
		/// <param name="level">日志级别</param>
		/// <param name="ex">异常</param>
		protected override void OnWrite(LogLevel level, Exception ex)
		{
			WriteLog(Control, WriteLogEventArgs.Current.Set(level, null, ex, true).ToString() + Environment.NewLine, MaxLines);
		}

		/// <summary>写异常日志</summary>
		/// <param name="level">日志级别</param>
		/// <param name="ex">异常</param>
		/// <param name="msg">信息</param>
		protected override void OnWrite(LogLevel level, Exception ex, String msg)
		{
			WriteLog(Control, WriteLogEventArgs.Current.Set(level, msg, ex, true).ToString() + Environment.NewLine, MaxLines);
		}

		/// <summary>写带格式信息的异常日志</summary>
		/// <param name="level">日志级别</param>
		/// <param name="ex">异常</param>
		/// <param name="formatMsg">格式化字符串</param>
		/// <param name="args">格式化参数</param>
		protected override void OnWrite(LogLevel level, Exception ex, String formatMsg, params Object[] args)
		{
			// 特殊处理异常对象
			if (ex == null && args != null && args.Length == 1 && args[0] is Exception && (formatMsg.IsNullOrWhiteSpace() || formatMsg == "{0}"))
			{
				WriteLog(Control, WriteLogEventArgs.Current.Set(level, null, args[0] as Exception, true).ToString() + Environment.NewLine, MaxLines);
			}
			else
			{
				WriteLog(Control, WriteLogEventArgs.Current.Set(level, Format(formatMsg, args), ex, true).ToString() + Environment.NewLine, MaxLines);
			}
		}

		/// <summary>在WinForm控件上输出日志，主要考虑非UI线程操作</summary>
		/// <remarks>不是常用功能，为了避免干扰常用功能，保持UseWinForm开头</remarks>
		/// <param name="control">要绑定日志输出的WinForm控件</param>
		/// <param name="msg">日志</param>
		/// <param name="maxLines">最大行数</param>
		public static void WriteLog(Control control, String msg, Int32 maxLines = 1000)
		{
			if (control == null) return;

			var txt = control as TextBoxBase;
			if (txt == null) throw new HmExceptionBase("不支持的控件类型{0}！", control.GetType());

			var func = new Action<String>(m =>
			{
				try
				{
					if (txt.Lines.Length >= maxLines) txt.Clear();

					//// 如果不是第一行，加上空行
					//if (txt.TextLength > 0) txt.AppendText(Environment.NewLine);

					//// 提前取得光标所在行索引，决定后面要不要滚动
					//var cur = txt.GetFirstCharIndexOfCurrentLine();
					//var line = txt.GetLineFromCharIndex(cur);
					// AppendText本身就会让文本滚动到最后，不需要额外的滚动代码

					// 记录原原则
					var selstart = txt.SelectionStart;
					var sellen = txt.SelectionLength;

					// 输出日志
					if (m != null)
					{
						//txt.AppendText(m);
						// 需要考虑处理特殊符号
						ProcessBell(ref m);
						ProcessBackspace(txt, ref m);
						ProcessReturn(txt, ref m);

						if (String.IsNullOrEmpty(m)) return;
						txt.AppendText(m);
					}

					// 如果有选择，则不要滚动
					if (sellen > 0)
					{
						// 恢复选择
						if (selstart < txt.TextLength)
						{
							sellen = Math.Min(sellen, txt.TextLength - selstart - 1);
							txt.Select(selstart, sellen);
							txt.ScrollToCaret();
						}

						return;
					}

					// 5行内滚动
					//if (line + 5 > txt.Lines.Length && txt.SelectionLength <= 0)
					//{
					// 取得最后一行首字符索引
					var lines = txt.Lines.Length;
					var last = lines <= 1 ? 0 : txt.GetFirstCharIndexFromLine(lines - 1);
					if (last >= 0)
					{
						// 滚动到最后一行第一个字符
						txt.Select(last, 0);
						txt.ScrollToCaret();
					}
					//}
				}
				catch { }
			});

			txt.Invoke(func, msg);
			//var ar = txt.BeginInvoke(func, msg);
			//ar.AsyncWaitHandle.WaitOne(100);
			//if (!ar.AsyncWaitHandle.WaitOne(10))
			//    txt.EndInvoke(ar);
		}

		static void ProcessBackspace(TextBoxBase txt, ref String m)
		{
			while (true)
			{
				var p = m.IndexOf('\b');
				if (p < 0) break;

				// 计算一共有多少个字符
				var count = 1;
				while (p + count < m.Length && m[p + count] == '\b') count++;

				// 前面的字符不足，消去前面历史字符
				if (p < count)
				{
					count -= p;
					// 选中最后字符，然后干掉它
					if (txt.TextLength > count)
					{
						txt.Select(txt.TextLength - count, count);
						txt.SelectedText = null;
					}
					else
						txt.Clear();
				}
				else if (p > count)
				{
					// 少输出一个
					txt.AppendText(m.Substring(0, p - count));
				}

				if (p == m.Length - count)
				{
					m = null;
					break;
				}
				m = m.Substring(p + count);
			}
		}

		/// <summary>处理回车，移到行首</summary>
		/// <param name="txt"></param>
		/// <param name="m"></param>
		static void ProcessReturn(TextBoxBase txt, ref String m)
		{
			while (true)
			{
				var p = m.IndexOf('\r');
				if (p < 0) break;

				// 后一个是
				if (p + 1 < m.Length && m[p + 1] == '\n')
				{
					txt.AppendText(m.Substring(0, p + 2));
					m = m.Substring(p + 2);
					continue;
				}

				// 后一个不是\n，移到行首
				if (p >= 0)
				{
					// 取得最后一行首字符索引
					var lines = txt.Lines.Length;
					var last = lines <= 1 ? 0 : txt.GetFirstCharIndexFromLine(lines - 1);
					if (last >= 0)
					{
						// 最后一行第一个字符，删掉整行
						txt.Select(last, txt.TextLength - last);
						txt.SelectedText = null;
					}
				}

				if (p + 1 == m.Length)
				{
					m = null;
					break;
				}
				m = m.Substring(p + 1);
			}
		}

		static void ProcessBell(ref String m)
		{
			var ch = (Char)7;
			var p = 0;
			while (true)
			{
				p = m.IndexOf(ch, p);
				if (p < 0) break;

				if (p > 0)
				{
					var str = m.Substring(0, p);
					if (p + 1 < m.Length) str += m.Substring(p + 1);
					m = str;
				}

				Console.Beep();
				//SystemSounds.Beep.Play();
			}
		}
	}
}